/**
  ******************************************************************************
  * @file         lcd.c
  * @brief        LCD组件任务功能逻辑
  * @author       wuyiqing
  * @date         2022-11-08
  * @version      v001
  * @copyright    dxtc
  * 
  * @note         鼎新同创·智能锁
  *
  ******************************************************************************
  */
#include "component.h"
#include "device.h"
#include "lcm.h"
#include "lcd.h"


#define LCD_LOG(format, ...) OSAL_LOG(C_LIGHT_PURPLE format C_NONE, ##__VA_ARGS__)

#define PIC_HEAD_SIZE 54 // 文件头大小
#define PIC_DEEP 16 // 图片位深
#define PIC_HEAD_FLAG 0x4D42 // BM
#define LCD_TOTAL_LEN (LCD_WIDTH*LCD_HEIGHT*2) // 全屏总的数据长度

#define EVENT_DISPLAY_PIC (0X00000001) /// 显示图片事件

#define BACK_LIGHT_VHW vPIN_C33


/* 当前图片在flash的索引信息 */
typedef struct
{
    uint32_t addr;   // 地址
    uint32_t length; // 长度
} PictureInfo_stu_t;

/* 当前显示的图片信息 */
typedef struct
{
    uint16_t headFlag;// 文件头标志
    uint8_t  headLen; // 文件头长度
    uint32_t dataLen; // 数据长度（文件长度-头长度）
    uint16_t width;   // 图宽
    uint16_t height;  // 图高
    uint8_t  deep;    // 图片位深
} BmpInfo_stu_t;

/* flash读取8-16的信息 */
static struct
{
    uint16_t m_nLangOffset; // 多语种的偏移量
    uint16_t m_nWavLenMax;  // 单个语音文件压缩后的最大值, 单位 KB
    uint16_t m_nBmpSnStart; // 图片文件的序号起始
    uint16_t m_nBmpFileNum; // 图片文件的个数
} BIN_FILE_HEADER;

/* 刷图的显示进度信息 */
static struct
{
    uint8_t  pic_num;   // 动图张数 单张为1
    uint8_t  cur_num;   // 当前显示张数
    uint32_t pic_times; // 显示多少张次 动图：张数*次数 单张为1
    uint32_t cur_times; // 当前显示多少次 单张为1
} displayStep;

//定义显存
__attribute__((aligned(16))) static uint8_t display_buffer[LCD_TOTAL_LEN];

static PictureInfo_stu_t picInfo;   // 图片信息
static BmpInfo_stu_t headInfo;      // 图片文件头读取信息


/**
  * @brief  获取图片在flash的信息
  * @note   
  */
static ErrorStatus Lcd_GetInfo(uint16_t sn, PictureInfo_stu_t *picInfo)
{
    uint8_t arr[16] = {0};
    LCD_LOG("sn:%d!\r\n", sn);
	uint32_t offsetAddr = (BIN_FILE_HEADER.m_nBmpSnStart + sn) * 8;

	Device_Read(vFLASH_0, picInfo, sizeof(PictureInfo_stu_t), offsetAddr);
	return SUCCESS;
}

/**
  * @brief  读取图片头信息
  * @note   
  */
static ErrorStatus Lcd_ReadHeadInfo(void)
{
    // 文件头处理
    static uint8_t headArr[100] = {0};
    Device_Read(vFLASH_0, headArr, 64, picInfo.addr);

    memcpy(&headInfo.headFlag, headArr, sizeof(headInfo.headFlag));
    LCD_LOG("headArr 0x%02x, %d, %d, %d\r\n", headArr[0], headArr[18], headArr[22], headArr[28]);

    headInfo.headLen = headArr[13] << 24 | headArr[12] << 16 | headArr[11] << 8 | headArr[10];
    headInfo.width = headArr[19] << 8 | headArr[18];
    headInfo.height = headArr[23] << 8 | headArr[22];
    headInfo.deep = headArr[28];
    headInfo.dataLen = picInfo.length - headInfo.headLen;

    LCD_LOG("headLen %d, width %d, height %d, deep %d, dataLen: %d\r\n", headInfo.headLen, headInfo.width, headInfo.height, headInfo.deep, headInfo.dataLen);

    if (headInfo.headFlag != PIC_HEAD_FLAG)
    {
        LCD_LOG("headInfo.headFlag error 0x%0X!\r\n", headInfo.headFlag);
        return ERROR;
    }

    if (headInfo.deep != PIC_DEEP)
    {
        LCD_LOG("headInfo.deep error %d!\r\n", headInfo.deep);
        return ERROR;
    }

	return SUCCESS;
}

/**
  * @brief  清空显示屏
  * @note   
  */
static void LCD_Clear(void)
{
    LCD_LOG("LCD_Clear\r\n");

    // 设置显示区域
    lcd_set_window(0, 0, LCD_WIDTH-1, LCD_HEIGHT-1);
    memset(display_buffer, 0, LCD_TOTAL_LEN);
    Device_Write(vSPI_3, display_buffer, LCD_TOTAL_LEN, LCD_DISPLAY_PIC);
}

/**
  * @brief  读取并设置图片信息和显示区域
  * @note   
  */
static ErrorStatus Lcd_SetPicInfo(uint16_t sn)
{
    // 获取图片文件信息
    Lcd_GetInfo(sn - 10000, &picInfo);
    LCD_LOG("picInfo.addr = %d \r\n", picInfo.addr);
    LCD_LOG("picInfo.length = %d \r\n", picInfo.length);
    return Lcd_ReadHeadInfo();
}

/**
 * @brief  开始刷图
 * @note 
 */
static void LCD_StartDisplay(void)
{
    if (displayStep.cur_times == displayStep.pic_times) // 结束
    {
        displayStep.cur_num = 0;
        displayStep.cur_times = 0;
        OSAL_EventDelete(COMP_LCD, EVENT_DISPLAY_PIC);//删除显示事件
        LCD_Clear(); 
        Device_Write(BACK_LIGHT_VHW, NULL, 0, 0);
    }
    else
    {
        if (displayStep.pic_num > 1 || (displayStep.cur_times & 1) == 0)
        {
            uint32_t start_addr = picInfo.addr + headInfo.headLen + picInfo.length * displayStep.cur_num;
            Device_Read(vFLASH_0, display_buffer, headInfo.dataLen, start_addr);
        }
        else
        {
            /* 刷黑图 （displayStep.pic_num为1时，表示显示单张图，刷一下黑图刷一下图片形成闪烁效果）*/
            memset(display_buffer, 0, headInfo.dataLen);
        }
        Device_Write(vSPI_3, display_buffer, headInfo.dataLen, LCD_DISPLAY_PIC);

        displayStep.cur_num++;
        displayStep.cur_times++;
        if (displayStep.cur_num == displayStep.pic_num)
        {
            displayStep.cur_num = 0;
        }
    }
}

/**
 * @brief  函数说明：显示动画
 * @note 
 */
static void LCD_DisplayGif(DisplayGif_stu_t *bmp)
{
    LCD_LOG("LCD_DisplayGif\r\n");

    if (Lcd_SetPicInfo(bmp->start_sn) == SUCCESS)
    {
        Device_Write(BACK_LIGHT_VHW, NULL, 0, 1);

        if (bmp->start_x == UINT16_MAX)// X居中
        {
            bmp->start_x = (headInfo.width >= LCD_WIDTH) ? 0 : ((LCD_WIDTH - headInfo.width) / 2);
        }

        if (bmp->start_y == UINT16_MAX)// Y居中
        {
            bmp->start_y = (headInfo.height >= LCD_HEIGHT) ? 0 : ((LCD_HEIGHT - headInfo.height) / 2);
        }
        LCD_Clear();
        lcd_set_window(bmp->start_x, bmp->start_y, (bmp->start_x + headInfo.width - 1), (bmp->start_y + headInfo.height - 1));

        displayStep.cur_num = 0;
        displayStep.cur_times = 0;
        displayStep.pic_num = bmp->end_sn - bmp->start_sn + 1;
        if (displayStep.pic_num == 1)
        {
            // 单张图（当bmp->times大于1时，表示单张图闪烁）
            displayStep.pic_times = bmp->times * 2 - 1;
        }
        else
        {
            // 多张动图
            displayStep.pic_times = displayStep.pic_num * bmp->times;
        }

        OSAL_EventDelete(COMP_LCD, EVENT_DISPLAY_PIC);
        OSAL_EventSingleCreate(COMP_LCD, EVENT_DISPLAY_PIC, 0, EVT_PRIORITY_MEDIUM);
        if (bmp->interval != UINT16_MAX)
        {
            OSAL_EventRepeatCreate(COMP_LCD, EVENT_DISPLAY_PIC, bmp->interval, EVT_PRIORITY_MEDIUM);
        }
    }
    else
    {
        LCD_LOG("picInfo illegal\r\n");
    }
}

/**
  * @brief  进入休眠函数
  * @note   
  */
static void Lcd_EnterSleep()
{
    Device_Write(BACK_LIGHT_VHW, NULL, 0, 0);
    lcd_enter_sleep();
    Device_Disable(vFLASH_0);
    Device_Disable(vSPI_3);
}

/**
  * @brief  邮箱处理函数
  * @note   
  */
static void Lcd_ProcessMbox(uint8_t *msg)
{
    switch (msg[0])
    {
    case LCD_MBOX_GIF:
        LCD_DisplayGif((DisplayGif_stu_t *)&msg[1]);
        break;

    case LCD_MBOX_STOP:
        Lcd_EnterSleep();
        break;
    }
}

/**
 * @brief  显示屏初始化
 * @note
 * @return
 */
static void LCD_init(void)
{
    static uint8_t init_flag = 0;
    LCD_LOG("LCD_init, init_flag= %d\r\n", init_flag);

    Device_Enable(vSPI_3);
    // Device_Enable(vFLASH_0);
    lcd_exit_sleep();
    Device_DelayMs(5);
    lcd_config_init();

    if (!init_flag)
    {
        Device_Read(vFLASH_0, &BIN_FILE_HEADER, sizeof(BIN_FILE_HEADER), 8);
        init_flag = 1;

        LCD_LOG("m_nLangOffset:%d\r\n",   BIN_FILE_HEADER.m_nLangOffset); // 多语种的偏移量
        LCD_LOG("m_nWavLenMax :%dKB\r\n", BIN_FILE_HEADER.m_nWavLenMax ); // 单个语音文件压缩后的最大值, 单位 KB
        LCD_LOG("m_nBmpSnStart:%d\r\n",   BIN_FILE_HEADER.m_nBmpSnStart); // 图片文件的序号起始
        LCD_LOG("m_nBmpFileNum:%d\r\n",   BIN_FILE_HEADER.m_nBmpFileNum); // 图片文件的个数
    }

    LCD_Clear();
}

/**
 * @brief  LCD任务函数
 *
 * @note
 *
 * @param  event：当前任务的所有事件
 *
 * @return 返回未处理的事件
 */
static uint32_t Lcd_Task(uint32_t event)
{
    /* 系统启动事件 */
    if (event & EVENT_SYS_START)
    {
        LCD_LOG("lcd task start\r\n");
        LCD_init();
        return (event ^ EVENT_SYS_START);
    }

    /* 系统休眠事件 */
	if (event & EVENT_SYS_SLEEP)
	{
		LCD_LOG("lcd task sleep\r\n");
        Lcd_EnterSleep();
		return ( event ^ EVENT_SYS_SLEEP );
	}

    /* 系统邮箱事件 */
    if (event & EVENT_SYS_MBOX)
    {
        uint8_t buffer[100] = {0};
        while (OSAL_MboxAccept(buffer))
        {
            Lcd_ProcessMbox(buffer);
        }
        return ( event ^ EVENT_SYS_MBOX );
    }

    /* 显示图片事件 */
    if (event & EVENT_DISPLAY_PIC)
    {
        LCD_LOG("lcd display pic\r\n");
        LCD_StartDisplay();
        return (event ^ EVENT_DISPLAY_PIC);
    }

    return 0;
}

COMPONENT_TASK_EXPORT(COMP_LCD, Lcd_Task, 0);
